#ifndef __LISTCPP__
#define __LISTCPP__
#include <iostream>
#include <stdexcept>
#include <string>
#include "List.h"

template<class T>
class ListCpp
{
    public:

        class LIter;
        class CLIter;
        class ErrList;

        ListCpp()throw(); // default ctor- make empty list
        ~ListCpp();//Default dtor
        ListCpp(const ListCpp& other)throw();//Copy ctor,throw if fail to create c ist
        ListCpp& operator=(const ListCpp& other)throw(); // Assignment operator
        bool     IsEmpty()const;// return true if list is empty ,else false

        /**funcs that workes with const iterator**/
        CLIter  Begin()const;//return const iterator to first element
        CLIter  End()const;//return const iterator to the past-the-end element

        // insert before target of const iterator,return iterator to data
        // if iterator target is not a data node or the End(),behavior is undefined
        LIter   Insert(CLIter _it,T* _data);
        LIter   Remove(CLIter _it) throw();// Remove iterator _it, return next iterator after the romved one
                                           //throw if removing from empty list

        /**funcs that workes with iterator**/
        LIter   Begin();//return iterator to first element
        LIter   End();//return iterator to the past-the-end element

    private:

        List* m_list;//c List
        void Copy(const ListCpp& _other);//copy list from other
        void CreateList()throw();//create list block with trowing
};

/******************************************************************************/
/**                     define the exception classes                         **/
/******************************************************************************/
template<typename T>
class   ListCpp<T>::ErrList:  public std::runtime_error
{
    public:

        ErrList(const std::string& msg = "") : runtime_error(msg) {}
};

/******************************************************************************/
/**                     define LIter class                                   **/
/******************************************************************************/
template<class T>
class ListCpp<T>::LIter
{
    public:

        //LIter();                        default CTOR forbidden
        //~LIter();                       DTOR provided by compiler
        //LIter(const LIter&)             copy CTOR provided by compiler
        //LIter& operator=(const LIter&)  assignment operator provided by compiler

        LIter& operator++();//step to next data node
        LIter& operator--();//step to previous data node
        T*     Get() const  ;      //return pointer to data in iterator's target

        friend LIter ListCpp<T>::Begin();
        friend LIter ListCpp<T>::End();
        friend LIter ListCpp<T>::Insert(CLIter _it,T* _data)throw();
        friend LIter ListCpp<T>::Remove(CLIter _it)throw();

        bool operator==(const CLIter& _iter);
        bool operator!=(const CLIter& _iter);

    private :

        LIter(const ListIter& _cListIter);//ctor from List c iter ,for use of class ListCpp

        #ifdef _DEBUG//in debug mode we chek if iterator is legal
            LIter(const ListIter& _cListIter,List* _cList);
            List* m_list;//list of the iterator
        #endif //_DEBUG

        ListIter m_iter;//c list iterator

    protected:
        const ListIter& GetIter()const;
};

/******************************************************************************/
/**                     define CLIter class                                   **/
/******************************************************************************/
template<class T>
class ListCpp<T>::CLIter :  private ListCpp<T>::LIter
{
    public:

        //CLIter();                         default CTOR forbidden
        //~CLIter();                        DTOR provided by compiler
        //CLIter(const CLIter&)             copy CTOR provided by compiler
        //CLIter& operator=(const CLIter&)  assignment operator provided by compiler

        using ListCpp<T>::LIter::operator++; //step to next data node
        using ListCpp<T>::LIter::operator--;//step to previous data node
        const T*    Get()const ;// return pointer to data in iterator's target
        CLIter(const ListCpp::LIter& _liter);// implicit conversion to const iterator
        bool operator==(const CLIter& _iter)const;
        bool operator!=(const CLIter& _iter)const;

        friend LIter ListCpp<T>::Insert(CLIter _it,T* _data)throw();
        friend LIter ListCpp<T>::Remove(CLIter _it) throw();

    private:
        using ListCpp<T>::LIter::GetIter;//return clist iter
};


/******************************************************************************/
/**                     implementation of class Iter                         **/
/******************************************************************************/
template<class T>
ListCpp<T>::LIter::LIter(const ListIter& _cListIter):m_iter(_cListIter){}

#ifdef _DEBUG//in debug mode we chek if iterator is legal
    template<class T>
    ListCpp<T>::LIter::LIter(const ListIter& _cListIter,List* _cList):m_iter(_cListIter),m_list(_cList){}
#endif //_DEBUG

template<class T>
typename ListCpp<T>::LIter&   ListCpp<T>::LIter::operator++()
{
    m_iter = ListIterNext(m_iter);
    return *this;
}

template<class T>
typename ListCpp<T>::LIter&   ListCpp<T>::LIter::operator--()
{
    m_iter = ListIterPrev(m_iter);
    return *this;
}

template<class T>
T*  ListCpp<T>::LIter::Get() const
{
    return  reinterpret_cast<T*>((ListGetData(m_iter)));
}

template<class T>
bool ListCpp<T>::LIter::operator==(const CLIter& _iter)
{
   return _iter.operator==(*this);
}

template<class T>
bool ListCpp<T>::LIter::operator!=(const CLIter& _iter)
{
    return _iter.operator!=(*this);
}

template<class T>
const ListIter& ListCpp<T>::LIter::GetIter()const
{
    return m_iter;
}

/******************************************************************************/
/**                     implementation of class CIter                        **/
/******************************************************************************/

template<class T>
const T*  ListCpp<T>::CLIter::Get()const
{
   return static_cast<const T*>( LIter::Get() );
}

template<class T>
ListCpp<T>::CLIter::CLIter(const ListCpp<T>::LIter& _liter):ListCpp<T>::LIter::LIter(_liter){}

template<class T>
bool ListCpp<T>::CLIter::operator==(const CLIter& _iter)const
{
    return ListIterIsEqual(GetIter(),_iter.GetIter());
}

template<class T>
bool ListCpp<T>::CLIter::operator!=(const CLIter& _iter)const
{
    return !operator==(_iter);
}

/******************************************************************************/
/**                     implementation of class ListCpp                      **/
/******************************************************************************/
template<class T>
ListCpp<T>::ListCpp()throw()
{
    CreateList();
}

template<class T>
ListCpp<T>::~ListCpp()
{
   listDestroy(m_list);
}

template<class T>
typename ListCpp<T>::LIter ListCpp<T>::Begin()
{
    return ListBegin(m_list);
}

template<class T>
typename ListCpp<T>::LIter ListCpp<T>::End()
{
   return ListEnd(m_list);
}

template<class T>
typename ListCpp<T>::CLIter ListCpp<T>::Begin()const
{
    return (const_cast< ListCpp<T>* >(this))->Begin();
}

template<class T>
typename ListCpp<T>::CLIter ListCpp<T>::End()const
{
    return (const_cast< ListCpp<T>* >(this))->End();
}

template<class T>
bool ListCpp<T>::IsEmpty()const
{
    return ListIsEmpty(m_list);
}

template<class T>
typename ListCpp<T>::LIter ListCpp<T>::Insert(CLIter _it,T* _data)
{
    return ListInsert(m_list,_it.GetIter(),_data);
}


template<class T>
typename ListCpp<T>::LIter ListCpp<T>::Remove(CLIter _it)throw()
{
    try
    {
        if(ListIsEmpty(m_list))
        {
            throw (typename ListCpp<T>::ErrList("remove from empty list"));
        }
    }
    catch(typename ListCpp<T>::ErrList& _err)
    {
        std::cout<<"end iterator insertion"<<std::cout;
        throw;
    }

    return  ListRemove(m_list,_it.GetIter());
}

template<class T>
void ListCpp<T>::Copy(const ListCpp& _other)
{
    for (ListCpp<T>::CLIter it = _other.Begin() ; it != _other.End() ;++it )
    {
       Insert(End(),const_cast<T*>(it.Get()));
    }
}

template<class T>
void ListCpp<T>::CreateList()throw()
{
    try
    {
        if ( !(m_list = ListNew() ) )
        {
           throw (typename ListCpp<T>::ErrList("remove from empty list"));
        }
    }
    catch(typename ListCpp<T>::ErrList& _err)
    {
        std::cout<<"end iterator insertion"<<std::cout;
        throw;
    }
}

template<class T>
ListCpp<T>& ListCpp<T>::operator=(const ListCpp& _other)throw()
{
    if (this!=&_other)
    {
        listDestroy(m_list);
        CreateList();
        Copy(_other);
    }
    return *this;
}

template<class T>
ListCpp<T>::ListCpp(const ListCpp& _other)throw()
{
    CreateList();
    Copy(_other);
}

#endif // __LISTCPP__
